package com.xxwu.springgobang.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxwu.springgobang.component.*;
import com.xxwu.springgobang.mapper.UserMapper;
import com.xxwu.springgobang.model.Room;
import com.xxwu.springgobang.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.RoundingMode;

@Slf4j
@Component
public class GameController extends TextWebSocketHandler {

    @Resource
    private OnlineUserManager onlineUserManager;

    @Resource
    private RoomManager roomManager;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        GameReadyResponds resp = new GameReadyResponds();

        //判断用户是否登陆
        //使用是否进入gameHall哈希表来判定是否登陆
        // 在进入游戏大厅页面时，客户端就向服务器发送了协议升级的请求，然后MatchController就开始工作将用户id加入了gameHall哈希表
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            resp.setReason("您还未登陆！");
            resp.setOk(false);
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        //判断用户是否进入房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            resp.setOk(false);
            resp.setReason("您尚未进入房间");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //判断用户是否多开
        //使用是否进入gameHall哈希表来判定
        // 在进入游戏大厅页面时，客户端就向服务器发送了协议升级的请求，然后MatchController就开始工作将用户id加入了gameHall哈希表
        //当玩家离开游戏大厅进入游戏房间时，上一个连接就已经断开，重新建立当前连接，若玩家还在游戏大厅哈希表，则判定为多开
        if(onlineUserManager.getSessionFromGameHall(user.getUserId()) != null
                || onlineUserManager.getSessionFromGameRoom(user.getUserId()) != null){
            log.info("getSessionFromGameHall:{}",onlineUserManager.getSessionFromGameHall(user.getUserId()));
            log.info("getSessionFromGameRoom{}",onlineUserManager.getSessionFromGameRoom(user.getUserId()));
            resp.setOk(true);
            resp.setReason("您已在游戏中！");
            resp.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
//            session.close();
            return;
        }

        //将玩家放进房间Map
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        //当前逻辑都是在大厅页面进行，以下是进行页面跳转到游戏房间进行的逻辑
        //玩家进入房间，当房间满时，通知客户端
        //虽然玩家1先进入了游戏，但还是得等待，等待左右玩家准备就绪才能开始游戏，->初始化棋盘
        synchronized (room){
            if(room.getUser1() == null){        //这里涉及到线程安全问题
                room.setUser1(user);
                //将先进入的玩家1设置为先手
                room.setIsWhite(user.getUserId());
                log.info("玩家1{}进入房间", user.getUsername());
                return;
            }
            //当前房间内已经有玩家1
            if(room.getUser2() == null){
                room.setUser2(user);
                log.info("玩家2{}进入房间", user.getUsername());

                //通知两个玩家游戏就绪
                //给玩家1通知
                noticeGameReady(room, room.getUser1(), room.getUser2());
                //给玩家2通知
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
            //还有玩家要进入房间
            //理论上不会发生，但为了增加程序的健壮性，进行这层逻辑判断
            resp.setOk(false);
            resp.setReason("房间已满，请重新匹配！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
        }
    }


    private void noticeGameReady(Room room, User user1, User user2) throws IOException {
        GameReadyResponds resp = new GameReadyResponds();
        resp.setOk(true);
        resp.setMessage("gameReady");
        resp.setThisUserId(user1.getUserId());
        resp.setThatUserId(user2.getUserId());
        resp.setRoomId(room.getRoomId());
        resp.setWhiteUser(room.getIsWhite());
        //将信息返回给客户端
        WebSocketSession session = onlineUserManager.getSessionFromGameRoom(user1.getUserId());
//        log.info("玩家{}的session信息", user1.getUsername());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

    }



    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            log.info("当前玩家未登录");
            return;
        }
        Room room = roomManager.getRoomByUserId(user.getUserId());
        room.putChess(message.getPayload());

    }


    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        try{
            //防止多开误伤
            User user = (User) session.getAttributes().get("user");
            WebSocketSession tmpSession = onlineUserManager.getSessionFromGameRoom(user.getUserId());
            if(tmpSession == session) {
                onlineUserManager.exitGameRoom(user.getUserId());
                log.info("玩家{}因异常离开游戏房间", user.getUsername());
            }
            }catch(NullPointerException e){
            log.info("异常离开房间时空指针");
//            GameReadyResponds resp = new GameReadyResponds();
//            resp.setOk(false);
//            resp.setReason("异常出现空指针" + e.getMessage());
//            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            return;
        }

        WebSocketSession tmpSession = onlineUserManager.getSessionFromGameRoom(user.getUserId());
        if(tmpSession == session) {
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("玩家{}退出房间", user.getUsername());

        noticeThatUser(user);
    }

    private void noticeThatUser(User user) throws IOException {
        // 获取房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        // 当前房间已解散
        if(room == null){
            return;
        }

        // 根据房间id获取对手
        User thatUser = user == room.getUser1() ? room.getUser2() : room.getUser1();
        // 看对手是否在线
        WebSocketSession session = onlineUserManager.getSessionFromGameRoom(thatUser.getUserId());
        if(session == null){
            log.info("对手已下线");
            return;
        }

        // 通知对手获胜
        GameResponds resp = new GameResponds();
        resp.setWinner(thatUser.getUserId());
        resp.setUserId(thatUser.getUserId());
        resp.setMessage("putChess");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        //更新玩家信息
        userMapper.updateWinner(thatUser.getUserId());
        userMapper.updateLoser(user.getUserId());
        // 销毁房间
        roomManager.remove(room.getRoomId(), user.getUserId(), thatUser.getUserId());
    }
}
